﻿// -----------------------------------------------------------------------
// <copyright file="LinearConvolution.cs" company="FCIS">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace LastVersionOfIpPackage.Operations
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using Enum_needed;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class LinearConvolution
    {
        /// <summary>
        /// static object of this class to use this without create object
        /// </summary>
        private static LinearConvolution linerConvolution = new LinearConvolution();

        /// <summary>
        /// Gets or sets the liner convlution.
        /// </summary>
        /// <value>
        /// The liner convlution.
        /// </value>
        public static LinearConvolution LinerConvalution
        {
            get { return LinearConvolution.linerConvolution; }
            set { LinearConvolution.linerConvolution = value; }
        }

        /// <summary>
        /// Adds the border.
        /// </summary>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="maskSize">Size of the mask.</param>
        /// <returns>
        /// new buffer
        /// </returns>
        public Color[,] AddBorder(Color[,] bufferOfImage, double maskSize)
        {
            try
            {
                int borderSize = (int)(((int)maskSize / 2) * 2);
                Color[,] newBuffer = new Color[bufferOfImage.GetLength(0) + borderSize, bufferOfImage.GetLength(1) + borderSize];
                for (int i = 0; i < bufferOfImage.GetLength(1) + borderSize; i++)
                {
                    for (int j = 0; j < bufferOfImage.GetLength(0) + borderSize; j++)
                    {
                        if ((i < (int)(maskSize / 2) || i >= bufferOfImage.GetLength(1) + (int)(maskSize / 2)) && (j < (int)(maskSize / 2) || j >= bufferOfImage.GetLength(0) + (int)(maskSize / 2)))
                        {
                            if (i < (int)(maskSize / 2) && j < (int)(maskSize / 2))
                            {
                                newBuffer[j, i] = bufferOfImage[0, 0];
                            }
                            else if (i < (int)(maskSize / 2) && j >= bufferOfImage.GetLength(0) + (int)(maskSize / 2))
                            {
                                newBuffer[j, i] = bufferOfImage[bufferOfImage.GetLength(0) - 1, 0];
                            }
                            else if (i >= bufferOfImage.GetLength(1) + (int)(maskSize / 2) && j < (int)(maskSize / 2))
                            {
                                newBuffer[j, i] = bufferOfImage[0, bufferOfImage.GetLength(1) - 1];
                            }
                            else if (i >= bufferOfImage.GetLength(1) + (int)(maskSize / 2) && j >= bufferOfImage.GetLength(0) + (int)(maskSize / 2))
                            {
                                newBuffer[j, i] = bufferOfImage[bufferOfImage.GetLength(0) - 1, bufferOfImage.GetLength(1) - 1];
                            }
                        }
                        else if (i < (int)(maskSize / 2))
                        {
                            newBuffer[j, i] = bufferOfImage[j - (int)(maskSize / 2), 0];
                        }
                        else if (i >= bufferOfImage.GetLength(1) + (int)(maskSize / 2))
                        {
                            newBuffer[j, i] = bufferOfImage[j - (int)(maskSize / 2), bufferOfImage.GetLength(1) - 1];
                        }
                        else if (j < (int)(maskSize / 2))
                        {
                            newBuffer[j, i] = bufferOfImage[0, i - (int)(maskSize / 2)];
                        }
                        else if (j >= bufferOfImage.GetLength(0) + (int)(maskSize / 2))
                        {
                            newBuffer[j, i] = bufferOfImage[bufferOfImage.GetLength(0) - 1, i - (int)(maskSize / 2)];
                        }
                        else
                        {
                            newBuffer[j, i] = bufferOfImage[j - (int)(maskSize / 2), i - (int)(maskSize / 2)];
                        }
                    }
                }

                return newBuffer;
            }
            catch (Exception ex)
            {
                LastVersionOfIpPackage.Logger.LogException(ex);
                throw ex;
            }
        }

        /// <summary>
        /// make Linears convolution with any mask to any picture.
        /// </summary>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="bufferOfImageWithPadding">The buffer of image with padding.</param>
        /// <param name="mask">The mask.</param>
        /// <param name="postOperation">The post operation.</param>
        /// <returns>
        /// the new image
        /// </returns>
        public Bitmap LinearConvolutionFn(Color[,] bufferOfImage, Color[,] bufferOfImageWithPadding, Mask mask, Enum_needed.PostProcesses postOperation)
        {
            try
            {
                double[,,] newColorBuffer = new double[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1), 3];
                Bitmap newImg = new Bitmap(bufferOfImage.GetLength(0), bufferOfImage.GetLength(1));
                int width = bufferOfImageWithPadding.GetLength(0) - (mask.MaskSize / 2);
                    int height = bufferOfImageWithPadding.GetLength(1) - (mask.MaskSize / 2);
                    int w = 0;
                    int h = 0;
                    double newRed = 0, newBlue = 0, newGreen = 0;
                    Color[,] newBufferOfImage = new Color[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
                    for (int i = mask.MaskSize / 2; i < height; i++, h++)
                    {
                        for (int j = mask.MaskSize / 2; j < width; j++, w++)
                        {
                            int tempHieght = -mask.MaskSize / 2;
                            int tempWidth = -mask.MaskSize / 2;
                            for (int im = 0; im < mask.MaskSize; im++)
                            {
                                for (int jm = 0; jm < mask.MaskSize; jm++)
                                {
                                    newRed += mask.MaskValues[jm, im] * (double)bufferOfImageWithPadding[j + tempWidth, i + tempHieght].R;
                                    newBlue += mask.MaskValues[jm, im] * (double)bufferOfImageWithPadding[j + tempWidth, i + tempHieght].B;
                                    newGreen += mask.MaskValues[jm, im] * (double)bufferOfImageWithPadding[j + tempWidth, i + tempHieght].G;
                                    tempWidth++;
                                }

                                tempWidth = -mask.MaskSize / 2;
                                tempHieght++;
                            }

                            if (postOperation == Enum_needed.PostProcesses.None)
                            {
                                newBufferOfImage[w, h] = Color.FromArgb((int)newRed, (int)newGreen, (int)newBlue);
                                newImg.SetPixel(w, h, newBufferOfImage[w, h]);
                                newRed = 0;
                                newBlue = 0;
                                newGreen = 0;
                            }
                            else if (postOperation == Enum_needed.PostProcesses.Normalization)
                            {
                                newColorBuffer[w, h, 0] = newRed;
                                newColorBuffer[w, h, 1] = newGreen;
                                newColorBuffer[w, h, 2] = newBlue;
                                newRed = 0;
                                newBlue = 0;
                                newGreen = 0;
                            }
                            else if (postOperation == Enum_needed.PostProcesses.Cutoff)
                            {
                                if (newRed > 255)
                                {
                                    newRed = 255;
                                }
                                else if (newRed < 0)
                                {
                                    newRed = 0;
                                }

                                if (newGreen > 255)
                                {
                                    newGreen = 255;
                                }
                                else if (newGreen < 0)
                                {
                                    newGreen = 0;
                                }

                                if (newBlue > 255)
                                {
                                    newBlue = 255;
                                }
                                else if (newBlue < 0)
                                {
                                    newBlue = 0;
                                }

                                newBufferOfImage[w, h] = Color.FromArgb((int)newRed, (int)newGreen, (int)newBlue);
                                newImg.SetPixel(w, h, newBufferOfImage[w, h]);
                                newRed = 0;
                                newBlue = 0;
                                newGreen = 0;
                            }
                        }

                        w = 0;
                    }

                    if (postOperation == Enum_needed.PostProcesses.Normalization)
                    {
                        newBufferOfImage = Normalize.Normalize11.Normalization(newColorBuffer);
                       newImg = BufferOfImage.Buffer.GetImage(newBufferOfImage);
                    }

                return newImg;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw ex;
            }
        }

        /// <summary>
        /// Linears the convolution orderd filter fn.
        /// </summary>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="bufferOfImageWithPadding">The buffer of image with padding.</param>
        /// <param name="maskSize">Size of the mask.</param>
        /// <param name="type">The type.</param>
        /// <returns>
        /// result image after order filter
        /// </returns>
        public Color[,] LinearConvolutionOrderdFilterFn(Color[,] bufferOfImage, Color[,] bufferOfImageWithPadding, int maskSize, OrderStatisticType type)
        {
            if (type == OrderStatisticType.AdaptiveMedian)
            {
                return this.LinearConvolutionAdptiveFilterFn(bufferOfImage, bufferOfImageWithPadding, maskSize);
            }

            double[,] redBuffer = new double[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
            double[,] greenBuffer = new double[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
            double[,] blueBuffer = new double[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
            int width = bufferOfImageWithPadding.GetLength(0) - (maskSize / 2);
            int height = bufferOfImageWithPadding.GetLength(1) - (maskSize / 2);
            int w = 0;
            int h = 0;
            Color[,] newBufferOfImage = new Color[bufferOfImage.GetLength(0), bufferOfImage.GetLength(1)];
            for (int i = maskSize / 2; i < height; i++, h++)
            {
                for (int j = maskSize / 2; j < width; j++, w++)
                {
                    int tempHieght = -maskSize / 2;
                    int tempWidth = -maskSize / 2;
                    Color[,] window = new Color[maskSize, maskSize];
                    for (int im = 0; im < maskSize; im++)
                    {
                        for (int jm = 0; jm < maskSize; jm++)
                        {
                            window[im, jm] = bufferOfImageWithPadding[j + tempWidth, i + tempHieght];
                            tempWidth++;
                        }

                        tempWidth = -maskSize / 2;
                        tempHieght++;
                    }

                    MixedColor[] xvalAndYVal = ApplicationOfSortAndMaxAndMin.Instance.GetValue(window, type);
                    if (type == OrderStatisticType.Midpoint)
                    {
                        int tempR = (window[xvalAndYVal[0].X, xvalAndYVal[0].Y].R + window[xvalAndYVal[1].X, xvalAndYVal[1].Y].R) / 2;
                        int tempG = (window[xvalAndYVal[0].X, xvalAndYVal[0].Y].R + window[xvalAndYVal[1].X, xvalAndYVal[1].Y].G) / 2;
                        int tempB = (window[xvalAndYVal[0].X, xvalAndYVal[0].Y].R + window[xvalAndYVal[1].X, xvalAndYVal[1].Y].B) / 2;
                        newBufferOfImage[w, h] = Color.FromArgb(tempR, tempG, tempB);
                    }
                    else
                    {
                        newBufferOfImage[w, h] = window[xvalAndYVal[0].X, xvalAndYVal[0].Y];
                    }
                }

                w = 0;
            }

            return newBufferOfImage;
        }

        /// <summary>
        /// Linears the convolution adptive filter fn.
        /// </summary>
        /// <param name="bufferOfImageOrginal">The buffer of image orginal.</param>
        /// <param name="bufferOfImage">The buffer of image after padding.</param>
        /// <param name="maskSize">Size of the mask.</param>
        /// <returns>
        /// new buffer of the image
        /// </returns>
        public Color[,] LinearConvolutionAdptiveFilterFn(Color[,] bufferOfImageOrginal, Color[,] bufferOfImage, int maskSize)
        {
            double[,] redBuffer = new double[bufferOfImageOrginal.GetLength(0), bufferOfImageOrginal.GetLength(1)];
            double[,] greenBuffer = new double[bufferOfImageOrginal.GetLength(0), bufferOfImageOrginal.GetLength(1)];
            double[,] blueBuffer = new double[bufferOfImageOrginal.GetLength(0), bufferOfImageOrginal.GetLength(1)];
            Bitmap newImg = new Bitmap(bufferOfImageOrginal.GetLength(0), bufferOfImageOrginal.GetLength(1));
            int width = bufferOfImage.GetLength(0) - (maskSize / 2);
            int height = bufferOfImage.GetLength(1) - (maskSize / 2);
            int w = 0;
            int h = 0;
            int tempMaskSize = 3;
            Color[,] newBufferOfImage = new Color[bufferOfImageOrginal.GetLength(0), bufferOfImageOrginal.GetLength(1)];
            for (int i = maskSize / 2; i < height; i++, h++)
            {
                for (int j = maskSize / 2; j < width; j++, w++)
                {
                    MixedColor min;
                    MixedColor max;
                    MixedColor median;
                    MixedColor center;
                    Color[,] window;
                    while (true)
                    {
                        int tempHieght = -tempMaskSize / 2;
                        int tempWidth = -tempMaskSize / 2;

                        window = new Color[tempMaskSize, tempMaskSize];
                        for (int im = 0; im < tempMaskSize; im++)
                        {
                            for (int jm = 0; jm < tempMaskSize; jm++)
                            {
                                window[im, jm] = bufferOfImage[j + tempWidth, i + tempHieght];
                                tempWidth++;
                            }

                            tempWidth = -tempMaskSize / 2;
                            tempHieght++;
                        }

                        ApplicationOfSortAndMaxAndMin filter = new ApplicationOfSortAndMaxAndMin();
                        MixedColor[] values = filter.GetValue(window, OrderStatisticType.AdaptiveMedian);
                        min = values[0];
                        max = values[1];
                        median = values[2];
                        center = values[3];

                        if (median.Value < max.Value && median.Value > min.Value)
                        {
                            break;
                        }
                        else
                        {
                            if (tempMaskSize < maskSize)
                            {
                                tempMaskSize += 2;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    if (center.Value < max.Value && center.Value > min.Value)
                    {
                        newBufferOfImage[w, h] = window[center.X, center.Y];
                    }
                    else
                    {
                        newBufferOfImage[w, h] = window[median.X, median.Y];
                    }

                    newImg.SetPixel(w, h, newBufferOfImage[w, h]);
                    tempMaskSize = 3;
                }

                w = 0;
            }

            return newBufferOfImage;
        }

        /// <summary>
        /// Linears the convolution geometric fn.
        /// </summary>
        /// <param name="image">The original image.</param>
        /// <param name="bufferOfImage">The buffer of image.</param>
        /// <param name="mask">The mask.</param>
        /// <returns>
        /// the result image after geometric operation
        /// </returns>
        public Color[,] LinearConvolutionGeometricFn(Bitmap image, Color[,] bufferOfImage, double mask)
        {
            Color[,] bufferOfImages = BufferOfImage.Buffer.GetBuffer(image);
            double[,] redBuffer = new double[bufferOfImages.GetLength(0), bufferOfImages.GetLength(1)];
            double[,] greenBuffer = new double[bufferOfImages.GetLength(0), bufferOfImages.GetLength(1)];
            double[,] blueBuffer = new double[bufferOfImages.GetLength(0), bufferOfImages.GetLength(1)];
            int width = bufferOfImage.GetLength(0) - (int)(mask / 2);
            int height = bufferOfImage.GetLength(1) - (int)(mask / 2);
            int w = 0;
            int h = 0;
            double newRed = 1, newBlue = 1, newGreen = 1;
            Color[,] newBufferOfImage = new Color[bufferOfImages.GetLength(0), bufferOfImages.GetLength(1)];
            for (int i = (int)mask / 2; i < height; i++, h++)
            {
                for (int j = (int)mask / 2; j < width; j++, w++)
                {
                    int tempHieght = -(int)mask / 2;
                    int tempWidth = -(int)mask / 2;
                    for (int im = 0; im < (int)mask; im++)
                    {
                        for (int jm = 0; jm < (int)mask; jm++)
                        {
                            double orgImgR, orgImgB, orgImgG;
                            orgImgR = (double)bufferOfImage[j + tempWidth, i + tempHieght].R;
                            orgImgB = (double)bufferOfImage[j + tempWidth, i + tempHieght].B;
                            orgImgG = (double)bufferOfImage[j + tempWidth, i + tempHieght].G;
                            if (bufferOfImage[j + tempWidth, i + tempHieght].R == 0)
                            {
                                orgImgR = 1;
                            }

                            if (bufferOfImage[j + tempWidth, i + tempHieght].B == 0)
                            {
                                orgImgB = 1;
                            }

                            if (bufferOfImage[j + tempWidth, i + tempHieght].G == 0)
                            {
                                orgImgG = 1;
                            }

                            newRed *= orgImgR;
                            newBlue *= orgImgB;
                            newGreen *= orgImgG;
                            tempWidth++;
                        }

                        tempWidth = -(int)mask / 2;
                        tempHieght++;
                    }

                    newRed = Math.Pow(newRed, (1 / (double)(mask * mask)));
                    newBlue = Math.Pow(newBlue, (1 / (double)(mask * mask)));
                    newGreen = Math.Pow(newGreen, (1 / (double)(mask * mask)));
                    newBufferOfImage[w, h] = Color.FromArgb((int)newRed, (int)newGreen, (int)newBlue);
                    newRed = 1;
                    newBlue = 1;
                    newGreen = 1;
                }

                w = 0;
            }

            return newBufferOfImage;
        }
    }
}
